package BGetPut(mkBGetPut, mkGetBPut, BGetS, BPutS, BGet, BPut, BGetPut, GetBPut,
         BClient, BServer, BClientS(..), BServerS(..),
         BClientServer, ClientBServer, mkBClientServer, mkClientBServer) where

--import RegCF
import ConfigReg
import GetPut
import Connectable
import ClientServer

--@ \subsubsection{BGetPut}
--@ \index{BGetPut@\te{BGetPut} (package)|textbf}
--@
--@ The interfaces \te{BGet} and \te{BPut} are similar to
--@ \te{Get} and \te{Put}, but the interconnection of them
--@ (via \te{Connectable} or in {\veri}) is implemented with a simple protocol
--@ that allows all inputs and outputs to be directly connected.
--@ All the ports are directly registered, without the logic of ready/enable handshaking.
--@ The protocol makes no assumptions about setup time and hold time for the
--@ registers at each end; so these interfaces may be used when the two ends
--@ have different clocks.  In all other circumstances, however, the
--@ \te{CGetPut} package will probably be preferable.  In particular, the
--@ BGetPut protocol is very slow.
--@
--@ The protocol consist of the sender putting the value to be sent on the
--@ \te{pvalue} output, and then toggling the \te{ppresent} wire.
--@ The receiver acknowledges the receipt by toggling the \te{gcredit} wire.
--@ Both \te{ppresent} and \te{gcredit} start out low.
--@ \begin{libverbatim}
--@ interface BGetS #(type sa);
--@     method Bit#(sa) gvalue();
--@     method Bool gpresent();
--@     method Action gcredit(Bool x1);
--@ endinterface: BGetS
--@ \end{libverbatim}
interface BGetS sa =
    gvalue   :: Bit sa            -- data
    gpresent :: Bool              -- toggles when new data is available
    gcredit  :: Bool -> Action    -- toggles when ready for new data

--@ \begin{libverbatim}
--@ interface BGetS #(type sa);
--@     method Bit#(sa) gvalue();
--@     method Bool gpresent();
--@     method Action gcredit(Bool x1);
--@ endinterface: BGetS
--@ \end{libverbatim}
interface BPutS sa =
    pvalue   :: Bit sa -> Action  -- new data
    ppresent :: Bool -> Action    -- toggles when new data has changed
    pcredit  :: Bool              -- toggles when ready for new data

--@ \begin{libverbatim}
--@ typedef BGetS#(SizeOf#(a)) BGet #(type a);
--@ typedef BPutS#(SizeOf#(a)) BPut #(type a);
--@
--@ typedef Tuple2 #(BGet#(a), Put#(a)) BGetPut #(type a);
--@ typedef Tuple2 #(Get#(a), BPut#(a)) GetBPut #(type a);
--@ \end{libverbatim}
type BGet a = BGetS (SizeOf a)
type BPut a = BPutS (SizeOf a)

type BGetPut a = (BGet a, Put a)
type GetBPut a = (Get a, BPut a)

--@ Create one end of the buffer.  Access to it is via a \te{Put} interface.
--@ \index{mkBGetPut@\te{mkBGetPut} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkBGetPut(Tuple2 #(BGetS#(sa), Put#(a)))
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
mkBGetPut :: (IsModule m c, Bits a sa) => m (BGetS sa, Put a)
mkBGetPut =
  liftModule $
  do
    latch     :: Reg (Bit sa) <- mkRegU

    srcData   :: Reg Bool <- mkConfigReg False -- mkRegCF False
    srcState  :: Reg Bool <- mkConfigReg False
    dstState  :: Reg Bool <- mkConfigReg False
    dstStateP :: Reg Bool <- mkConfigReg False

    addRules $
        rules
          {-# ASSERT no implicit conditions #-}
          {-# ASSERT fire when enabled #-}
          "BGetPut":
            when srcData
             ==> action
                    srcData := (dstStateP == dstState)
    return $
        (interface BGetS
            gvalue = latch
            gpresent = srcState
            gcredit b =
              action
                dstState := b
                dstStateP := dstState
        ,interface Put
            put x = action
                        latch := pack x
                        srcState := not srcState
                        srcData := True
                when not srcData
        )

--@ Create the other end of the buffer.  Access to it is via a \te{Get} interface.
--@ \index{mkGetBPut@\te{mkGetBPut} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkGetBPut(Tuple2 #(Get#(a), BPutS#(sa)))
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
mkGetBPut :: (IsModule m c, Bits a sa) => m (Get a, BPutS sa)
mkGetBPut =
  liftModule $
  do
    value     :: Reg (Bit sa) <- mkConfigRegU
    latch     :: Reg (Bit sa) <- mkConfigRegU

    dstData   :: Reg Bool <- mkConfigReg False
    dstState  :: Reg Bool <- mkConfigReg False
    srcState  :: Reg Bool <- mkConfigReg False
    srcStateP :: Reg Bool <- mkConfigReg False

    doneSrcState :: Reg Bool <- mkConfigReg False

    addRules $
        rules
          {-# ASSERT no implicit conditions #-}
          {-# ASSERT fire when enabled #-}
          "GetBPut":
            when doneSrcState /= srcStateP && not dstData
             ==> action
                    latch := value
                    dstData := True
                    dstState := not dstState
    return $
        (interface Get
            get = do
                    dstData := False
                    doneSrcState := not doneSrcState
                    return (unpack latch)
                when dstData
        ,interface BPutS
            pvalue v = value := v
            ppresent p =
              action
                srcState := p
                srcStateP := srcState
            pcredit = dstState
        )

--@ The \te{BGet} and \te{BPut} interface are connectable.
--@ \begin{libverbatim}
--@ instance Connectable #(BGetS#(sa), BPutS#(sa));
--@ \end{libverbatim}
instance Connectable (BGetS sa) (BPutS sa)
   where
    mkConnection :: (IsModule m c) => BGetS sa -> BPutS sa -> m Empty
    mkConnection g p =
        addRules $
         rules
          {-# ASSERT no implicit conditions #-}
          {-# ASSERT fire when enabled #-}
          "moveBGetPut":
            when True
             ==> action
                    p.pvalue g.gvalue
                    p.ppresent g.gpresent
                    g.gcredit p.pcredit

--@ \lineup
--@ \begin{libverbatim}
--@ instance Connectable #(BPutS#(sa), BGetS#(sa));
--@ \end{libverbatim}
instance Connectable (BPutS sa) (BGetS sa)
   where
    mkConnection p g = mkConnection g p

--@ The same idea may be extended  to clients and servers.

interface BClientS sa sb =
   request :: BGetS sa
   response:: BPutS sb

interface BServerS sa sb =
   request  :: BPutS sa
   response :: BGetS sb

instance Connectable (BClientS sa sb) (BServerS sa sb)
   where
    mkConnection :: (IsModule m c) => (BClientS sa sb) -> (BServerS sa sb) -> m Empty
    mkConnection c s = do
      c.request <-> s.request
      c.response <-> s.response

instance Connectable (BServerS sa sb) (BClientS sa sb)
   where
    mkConnection s c = mkConnection c s

--@ \begin{libverbatim}
--@ typedef BClientS#(SizeOf#(a), SizeOf#(b)) BClient #(type a, type b);
--@ typedef BServerS#(SizeOf#(a), SizeOf#(b)) BServer #(type a, type b);

--@ typedef Tuple2 #(BClient#(a, b), Server#(a, b)) BClientServer #(type a, type b);
--@ typedef Tuple2 #(Client#(a, b), BServer#(a, b)) ClientBServer #(type a, type b);
--@ \end{libverbatim}
type BClient a b = BClientS (SizeOf a) (SizeOf b)
type BServer a b = BServerS (SizeOf a) (SizeOf b)
type BClientServer a b = (BClient a b, Server a b)
type ClientBServer a b = (Client a b, BServer a b)
{-
--@ A \te{BClient} can be connected to a \te{BServer}
--@ and vice versa.
--@ \begin{libverbatim}
--@ instance Connectable #(BClientS#(a, b), BServerS#(a, b));
--@ instance Connectable #(BServerS#(a, b), BClientS#(a, b));
--@ \end{libverbatim}
instance Connectable (BClientS a b) (BServerS a b)
   where
    mkConnection :: (IsModule m c) => BClientS a b -> BServerS a b -> m Empty
    mkConnection c s =
       module
          rules
            "BClientServerRequest":when True
             ==> action
                    s.request.pvalue c.request.gvalue
                    s.request.ppresent c.request.gpresent
                    c.request.gcredit s.request.pcredit

            "BClientServerResponse":when True
             ==> action
                    c.response.pvalue s.response.gvalue
                    c.response.ppresent s.response.gpresent
                    s.response.gcredit c.response.pcredit

instance Connectable (BServerS a b) (BClientS a b)
   where
    mkConnection s c = mkConnection c s
-}
--@ \index{mkClientBServer@\te{mkClientBServer} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkClientBServer(Tuple2 #(Client#(a, b), BServerS#(sa, sb)))
--@   provisos (Bits#(a, sa), Bits#(b, sb));
--@ \end{libverbatim}
mkClientBServer :: (IsModule m c, Bits a sa, Bits b sb) =>
                   m (Client a b, BServerS sa sb)
mkClientBServer =
  module
    (g, cp) <- mkGetBPut
    (cg, p) <- mkBGetPut
    interface
     (interface Client
        request = g
        response = p
      ,
      interface BServerS
        request = cp
        response = cg
     )

--@ \begin{libverbatim}
--@ module mkBClientServer(Tuple2 #(BClientS#(sa, sb), Server#(a, b)))
--@   provisos (Bits#(a, sa), Bits#(b, sb));
--@ \end{libverbatim}
--@ \index{mkBClientServer@\te{mkBClientServer} (function)|textbf}
mkBClientServer :: (IsModule m c, Bits a sa, Bits b sb) =>
                   m (BClientS sa sb, Server a b)
mkBClientServer =
  module
    (g, cp) <- mkGetBPut
    (cg, p) <- mkBGetPut
    interface
     (interface BClientS
        request = cg
        response = cp
      ,
      interface Server
        request = p
        response = g
     )


